# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Windows implementations of platform-specific utilities and pseudo-constants

Certain functions must be implemented in OS-specific ways due to differences
between Linux and Windows.  The constants and functions defined here are
imported into a common interface by platform.py.

"""


import codecs
import os

from errno import ENOENT

import win32api
import win32com.shell.shell
import win32com.shell.shellcon
import win32con

# As of Python 2.4, Win32 error codes overwrite the OSError.errno
# values when a WinError is raised.  This is expected to be fixed in
# 2.5, in which case the default value of ENOENT will apply instead.
ENOENT_codes = [ENOENT, 3]

DEFAULT_EDITOR_COMMAND = 'notepad'


def DescribeSubprocessError(cmd, status):
  """Return a string describing an error returned by a subprocess.

  Arguments:
    cmd    -- the name or description of the command that was executed
    status -- the status code returned by the executed command

  """

  return "'%s' exited %d" % (cmd, status)


# No, svn_io_remove_dir2 doesn't work any better at all; see
# epg/hack-rmtree@431 for proof.
def rmtree(path):
  """Recursively delete a directory and all its contents, even if read-only.

  shutil.rmtree() doesn't work on Windows if any of the files or directories
  are read-only, which svn repositories are.  We need to be able to force the
  files to be writable (i.e., deletable) as we traverse the tree.

  This doesn't work very well, either.  os.remove (and maybe os.rmdir,
  too, epg doesn't remember) randomly fails to delete files yet raises
  no error, and SetFileAttributes randomly dies with permission
  denied.  Later attempts to delete the tree, even within the same
  process, seem to work sometimes.

  So, this suppresses all failures.

  Arguments:
    path -- a path to a directory to be deleted

  Raises:
    nothing, ever; sad, innit?
  """
  try:
    for fn in os.listdir(path):
      fullpath = os.path.join(path, fn)
      if os.path.isfile(fullpath):
        win32api.SetFileAttributes(fullpath, win32con.FILE_ATTRIBUTE_NORMAL)
        os.remove(fullpath)
      elif os.path.isdir(fullpath):
        rmtree(fullpath)

    win32api.SetFileAttributes(path, win32con.FILE_ATTRIBUTE_NORMAL)
    os.rmdir(path)
  except Exception:
    pass


def GetUserName():
  """Return the current user's local username."""
  return win32api.GetUserName()


def ExecutableNames(short_name):
  """Return a list of potential executable names based on the given short name.

  Arguments:
    short_name -- a candidate executable name, with no "dot" extension

  Returns:
    A list containing the given short name with each of the possible
    executable extensions (.exe, .bat, .com) appended.  For example,
    'command' => ['command.exe', 'command.bat', 'command.com']

  """

  # XXX(pamg): Is this really the best way to do this?  GetBinaryType() doesn't
  # appear to be available in win32api.
  namelist = []
  for ext in ['exe', 'bat', 'com']:
    namelist.append('.'.join((short_name, ext)))
  return namelist


def EditorWantsBOM(editor, encoding):
  return ('notepad' in editor
          and codecs.lookup(encoding) == codecs.lookup('utf-8'))


def _GetAllAppdata():
  return win32com.shell.shell.SHGetFolderPath(0,
                                  win32com.shell.shellcon.CSIDL_COMMON_APPDATA,
                                              0, 0)
def _GetUserAppdata():
  return win32com.shell.shell.SHGetFolderPath(0,
                                         win32com.shell.shellcon.CSIDL_APPDATA,
                                              0, 0)

def GetSvnUserConfigDir():
  return os.path.join(_GetUserAppdata(), 'Subversion')
def GetSvnSystemConfigDir():
  return os.path.join(_GetAllAppdata(), 'Subversion')
def GetGvnUserConf():
  return os.path.join(_GetUserAppdata(), 'gvn')
def GetGvnSystemConf():
  return os.path.join(_GetAllAppdata(), 'gvn')

class StrippedCRFile(object):
  """File-like object wrapper that strips CRs from CRLF.

  svn.diff writes out CRLF-terminated lines on Windows, which happens to
  work well for svn because it uses apr file objects, which apparently do
  no newline translation of their own (despite 30 (?) years of stdio
  precedent, *grumble*).  But, our file objects do such translation on
  Windows, so we need to turn all CRLFs into LFs before letting the file
  object add the CRs back.  Sigh.

  This only implements the write method.
  """
  def __init__(self, fp):
    self.fp = fp
  def write(self, s):
    return self.fp.write(s.replace('\r\n', '\n'))
